import 'package:flutter/material.dart';

class ShakeAnimationWidget extends StatefulWidget {
  final Widget child;
  final double shakeRange;
  final int shakeCount;
  final bool isForward;
  final ShakeAnimationController shakeAnimationController;
  ShakeAnimationWidget(
      {Key key,
      @required this.child,
      this.shakeCount = 0,
      this.shakeRange = 0.1,
      this.shakeAnimationController,
      this.isForward = true});

  @override
  State<StatefulWidget> createState() => _ShakeAnimationWidgetState();
}

class _ShakeAnimationWidgetState extends State<ShakeAnimationWidget>
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  Animation<double> _angleAnimation;
  int _shakeTotalCount = 0;
  int _shakeCurrentCount = 0;
  double _shakeRange;

  @override
  void initState() {
    super.initState();
    _shakeTotalCount = widget.shakeCount;
    _shakeRange = widget.shakeRange;
    if (_shakeRange <= 0) {
      _shakeRange = 0.0;
    } else if (_shakeRange > 1.0) {
      _shakeRange = 1.0;
    }

    _animationController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 200));
    _angleAnimation = TweenSequence<double>([
      TweenSequenceItem<double>(
          tween: Tween(begin: 0, end: _shakeRange), weight: 1),
      TweenSequenceItem<double>(
          tween: Tween(begin: _shakeRange, end: 0), weight: 2),
      TweenSequenceItem<double>(
          tween: Tween(begin: 0, end: -_shakeRange), weight: 3),
      TweenSequenceItem<double>(
          tween: Tween(begin: -_shakeRange, end: 0), weight: 4),
    ]).animate(_animationController);

    _angleAnimation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        _animationController.reverse();
      } else if (status == AnimationStatus.dismissed) {
        if (_shakeTotalCount == 0) {
          _animationController.forward();
        } else {
          if (widget.shakeAnimationController != null) {
            widget.shakeAnimationController.animationRunging = false;
          }
        }
        _shakeCurrentCount++;
      }
    });
    if (widget.shakeAnimationController != null) {
      widget.shakeAnimationController.setShakeListener((isOpen, shakeCount) {
        if (isOpen) {
          _shakeTotalCount = shakeCount;
          _animationController.reset();
          _animationController.forward();
        } else {
          _shakeTotalCount = widget.shakeCount;
          _animationController.stop();
          /// 监控停止动作
        }
      });
    }

    if (widget.isForward) {
      _animationController.forward();
      if (widget.shakeAnimationController != null) {
        widget.shakeAnimationController.animationRunging = true;
      }
    }
  }

  @override
  void dispose() {
    _animationController?.dispose();
    widget.shakeAnimationController?.removeListener();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return ShakeAnimationBuilder(
        child: widget.child, animation: _angleAnimation);
  }
}

class ShakeAnimationBuilder extends StatelessWidget {
  final Widget child;
  final Animation<double> animation;
  ShakeAnimationBuilder(
      {Key key, @required this.child, @required this.animation})
      : super(key: key);

  @override
  Widget build(Object context) {
    return AnimatedBuilder(
        animation: animation,
        builder: (context, child) {
          return Transform(
            transform: buildMatrix4(),
            alignment: Alignment.center,
            child: this.child,
          );
        });
  }

  /// 矩阵变化
  Matrix4 buildMatrix4() {
    double dx = 0;
    double dy = 0;
    dx = animation.value * 15;

    return Matrix4.translationValues(dx, dy, 0);
  }
}

typedef ShakeAnimationListener = void Function(bool isOpen, int shakeCount);

class ShakeAnimationController {
  bool animationRunging = false;
  ShakeAnimationListener _shakeAnimationListener;
  setShakeListener(ShakeAnimationListener listener) {
    _shakeAnimationListener = listener;
  }

  void start({int shakeCount = 1}) {
    if (_shakeAnimationListener != null) {
      animationRunging = true;
      _shakeAnimationListener(true, shakeCount);
    }
  }

  void stop() {
    if (_shakeAnimationListener != null) {
      animationRunging = false;
      _shakeAnimationListener(false, 0);
    }
  }

  void removeListener() {
    _shakeAnimationListener = null;
  }
}
